package com.crowdgeeks.common.util;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * 공통 Utility Class
 * @author raiden
 * @version v1.0
 */
public final class CommonUtil {

	// --------------------------------------------------------------------------
	// NULL 관련 UTIL
	// --------------------------------------------------------------------------

	/**
	 * null이면 true를 반환하다. 단 공백일때는 false를 반환한다. Parameter Parsing이나 DB에서 데이터를 읽어올
	 * 때 유용하게 쓰일 것이다.
	 * @param s null인지 확인하고 싶은 String 값
	 * @return null이면 true반환, 아니면 false 반환
	 */
	public static boolean isNull(String str) {
		if(str == null) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * null이거나 공백일때 true를 반환하다. Parameter Parsing이나 DB에서 데이터를 읽어올 때 유용하게 쓰일 것이다.
	 * @param str null인지 공백인지 확인하고 싶은 String 값
	 * @return null이거나 공백이면 true반환, 아니면 false 반환
	 */
	public static boolean isNullOrSpace(String str) {
		if(str == null || str.trim().length() == 0) {
			return true;
		} else {
			return false;
		}
	}

	public static boolean isNullOrBlank(String str) {
		return isNullOrSpace(str);
	}

	public static String null2Blank(String str) {
		if(str == null) {
			return "";
		}
		return str;
	}

	public static String null2String(Object obj, String str) {
		if(obj == null)
			return str;
		else if(obj instanceof String)
			return (String) obj;
		else
			return obj.toString();
	}

	public static String null2String(Object obj) {
		return null2String(obj, "");
	}

	public static String null2nbsp(String str) {
		if(isNullOrSpace(str)) {
			return "&nbsp;";
		}
		return str;
	}

	// --------------------------------------------------------------------------
	// STRING 관련 UTIL
	// --------------------------------------------------------------------------

	/**
	 * 문자열이 길때 어떤 특정한 길이에서 짤라서 반환한다. 짜른 뒤에 "..."를 붙인다. 단, 한글이나 영문이나 모두 1글자로
	 * 취급한다.
	 * @param s 짜르고 싶은 문장
	 * @param i 짜르고 싶은 길이
	 * @return 일정길이로 짜른 문자열에 "..."를 붙여 반환한다.
	 */
	public static String cutString(String s, int i) {
		String s1 = s;
		if(s1.length() > i)
			s1 = s.substring(0, i) + "...";
		return s1;
	}

	/**
	 * 문자열이 길때 어떤 특정한 길이에서 짤라서 반환한다. 짜른 뒤에 "..."를 붙인다. 단, 한글은 2바이트, 영문은 1바이트로
	 * 취급하여 반올림해서 짜른다.
	 * @param s 짜르고 싶은 문장
	 * @param i 짜르고 싶은 길이
	 * @return 일정길이로 짜른 문자열에 "..."를 붙여 반환한다.
	 */
	public static String cutString2Byte(String s, int i) {
		if(i < 4)
			return s;
		int j = 0;
		StringBuffer stringbuffer = new StringBuffer();
		for (int k = 0; k < s.length(); k++) {
			char c = s.charAt(k);
			if(c < '\uAC00' || '\uD7A3' < c)
				j++;
			else
				j += 2;
			stringbuffer.append(c);
			if(j <= i - 3)
				continue;
			stringbuffer.append("...");
			break;
		}

		return stringbuffer.toString();
	}

	public static String tagTranslate(String s) {
		if(s == null || s.trim().equals(""))
			return "";
		StringBuffer stringbuffer = new StringBuffer(s.length());
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if(c == '<')
				stringbuffer.append("&lt;");
			else if(c == '>')
				stringbuffer.append("&gt;");
			else if(c == '"')
				stringbuffer.append("&quot;");
			else if(c == '&')
				stringbuffer.append("&amp;");
			else if(c == '\'')
				stringbuffer.append("&#039");
			else
				stringbuffer.append(c);
		}

		return stringbuffer.toString();
	}

	public static List<String> split(String sourceString, String token) {
		if(sourceString == null || sourceString.trim().equals(""))
			return null;
		List<String> lsSplit = new ArrayList<String>();
		String[] strData = sourceString.split("[" + token + "]");

		for (int indexI = 0; indexI < strData.length; indexI++) {
			lsSplit.add(strData[indexI]);
		}
		return lsSplit;
	}

	public static String enter2brTag(String s) {
		if(s == null || s.trim().equals(""))
			return "";
		StringTokenizer stringtokenizer = new StringTokenizer(s, "\n");
		StringBuffer stringbuffer = new StringBuffer();
		for (; stringtokenizer.hasMoreTokens(); stringbuffer.append(stringtokenizer.nextToken() + "<br>"));
		return stringbuffer.toString();
	}

	// --------------------------------------------------------------------------
	// NUMBER 관련 UTIL
	// --------------------------------------------------------------------------

	/**
	 * request 받은 문자가 null 이면 target(int)값을 반환한다.
	 * @param requestParameter
	 * @param target
	 * @return
	 * @throws Exception
	 */
	public static int convertNull2Int(String requestParameter, int target) throws Exception {

		if(requestParameter == null) {
			return target;
		} else {
			return parseInt(requestParameter);
		}
	}

	/**
	 * String형 숫자를 int형 숫자로 변환한다. 변환이 안되는 String은 0로 반환한다.
	 * @param s int로 바꿀 String
	 * @return int형으로 바뀐 String
	 */
	public static int parseInt(String s) {
		try {
			if(s == null || s.equals(""))
				return 0;
			else
				return Integer.parseInt(s);
		} catch (Exception exception) {
			return 0;
		}
	}

	public static long parseLong(String s) {
		try {
			if(s == null || s.equals(""))
				return 0;
			else
				return Long.parseLong(s);
		} catch (Exception exception) {
			return 0;
		}
	}

	public static float parseFloat(String s) {
		try {
			if(s == null || s.equals(""))
				return 0;
			else
				return Float.parseFloat(s);
		} catch (Exception exception) {
			return 0;
		}
	}

	/**
	 * String형 숫자를 double형 숫자로 변환한다. 변환이 안되는 String은 0로 반환한다.
	 * @param s double로 바꿀 String
	 * @return double형으로 바뀐 String
	 */
	public static double parseDouble(String s) {
		if(s.equals("")) {
			return 0.0D;
		} else {
			try {
				return Double.valueOf(s).doubleValue();
			} catch (Exception e) {
				return 0.0D;
			}
		}
	}

	/**
	 * 통화형식처럼 숫자 3자리미다 ,(콤마)를 찍는다. double(int)형 인자를 받는다.
	 * @param money double(int)형 통화형식
	 * @return 3자리마다 ,(콤마)가 찍힌 형식
	 */
	public static String moneyFormat(double d) {
		NumberFormat numberformat = NumberFormat.getNumberInstance();
		String s = numberformat.format(d);
		return s;
	}

	/**
	 * 통화형식처럼 숫자 3자리미다 ,(콤마)를 찍는다. String형 인자를 받는다.
	 * @param money String형 통화형식
	 * @return 3자리마다 ,(콤마)가 찍힌 형식
	 */
	public static String moneyFormat(String s) {
		NumberFormat numberformat = NumberFormat.getNumberInstance();
		String s1;
		try {
			Number number = numberformat.parse(s);
			s1 = numberformat.format(number);
		} catch (ParseException parseexception) {
			s1 = "0";
		}
		return s1;
	}

	/**
	 * 대상문자열안의 논리적 값이 숫자를 표현하면 true, 아니면 false를 반환한다.
	 * @param str 대상문자열
	 * @return 대상문자열의 논리적 값이 숫자이면 true, 아니면 false
	 */
	public static boolean isStringIsNumeric(String str) {
		try {
			NumberFormat nf = NumberFormat.getInstance();
			// Number number = nf.parse(str);
			nf.parse(str);
			return true;
		} catch (ParseException parseexception) {
			return false;
		}
	}

	/**
	 * 대상문자열안의 논리적 숫자앞에 원하는 size의 자릿수에 맞게 '0'를 붙인다. 예) 000000000 (일억자리수)에 345를
	 * 넣었을 경우 '000000345'반환
	 * @param str 대상문자열
	 * @param size 원하는 자릿수
	 * @return 대상문자열에 자릿수 만큼의 '0'를 붙인 문자열
	 */
	public static String zerofill(String str, int size) {
		try {
			NumberFormat nf = NumberFormat.getInstance();
			return zerofill(nf.parse(str), size);
		} catch (Exception e) {
			return "";
		}

	}

	/**
	 * int형 숫자앞에 원하는 size의 자릿수에 맞게 '0'를 붙인다. 예) 000000000 (일억자리수)에 345를 넣었을 경우
	 * '000000345'반환
	 * @param str 대상 int형 숫자
	 * @param size 원하는 자릿수
	 * @return 대상문자열에 자릿수 만큼의 '0'를 붙인 문자열
	 */
	public static String zerofill(int num, int size) {
		return zerofill(new Integer(num), size);
	}

	/**
	 * double형 숫자앞에 원하는 size의 자릿수에 맞게 '0'를 붙인다. 예) 000000000 (일억자리수)에 345를 넣었을
	 * 경우 '000000345'반환
	 * @param str 대상 double형 숫자
	 * @param size 원하는 자릿수
	 * @return 대상문자열에 자릿수 만큼의 '0'를 붙인 문자열
	 */
	public static String zerofill(double num, int size) throws Exception {
		return zerofill(new Double(num), size);
	}

	/**
	 * Number형 숫자앞에 원하는 size의 자릿수에 맞게 '0'를 붙인다. 예) 000000000 (일억자리수)에 345를 넣었을
	 * 경우 '000000345'반환
	 * @param num 대상 Number형 숫자
	 * @param size 원하는 자릿수
	 * @return 대상문자열에 자릿수 만큼의 '0'를 붙인 문자열
	 */
	public static String zerofill(Number num, int size) {
		String zero = "";
		for (int i = 0; i < size; i++) {
			zero += "0";
		}
		DecimalFormat df = new DecimalFormat(zero);
		return df.format(num);
	}

	// --------------------------------------------------------------------------
	// DATE 관련 UTIL
	// --------------------------------------------------------------------------

	public static Timestamp makeTimestamp(String string) throws Exception {
		return makeTimestamp(string, "yyyy-MM-dd HH:mm:ss");
	}

	public static Timestamp makeTimestamp(String string, String format) throws Exception {
		SimpleDateFormat sourceFormatter = new SimpleDateFormat(format);
		Date date = sourceFormatter.parse(string);
		return new Timestamp(date.getTime());
	}

	/**
	 * java.sql.Timestamp형태의 날짜를 "yyyy-mm-dd hh:mm:ss"형의 날짜로 변환하여 반환한다.
	 * @param ts java.sql.Timestamp형태의 날짜
	 * @return "yyyy-MM-dd hh:mm:ss"형의 날짜
	 */
	public static String getSQLDate(Timestamp ts) {
		if(ts == null)
			return "";
		else
			return ts.toString();
	}

	public static String getDate(Timestamp ts) {
		SimpleDateFormat simpledateformat = new SimpleDateFormat("yyyy-MM-dd");
		return simpledateformat.format(ts);
	}

	public static String getDate(Timestamp ts, String format) {
		SimpleDateFormat simpledateformat = new SimpleDateFormat(format);
		return simpledateformat.format(ts);
	}

	/**
	 * "yyyy-MM-dd hh:mm:ss"형태의 날짜를 java.sql.Timestamp형의 날짜로 변환하여 반환한다.
	 * @param date "yyyy-MM-dd hh:mm:ss"형태의 날짜
	 * @return java.sql.Timestamp형의 날짜
	 */
	public static Timestamp setSQLDate(String date) {

		if(isNullOrSpace(date))
			return null;
		else if(isYMDHMSF(date))
			return Timestamp.valueOf(date);
		else if(isYMDHMS(date))
			return Timestamp.valueOf(date + ".0");
		else if(isYMD(date))
			return Timestamp.valueOf(date + " 00:00:00.0");
		else
			return null;

	}

	public static boolean isYMDHMSF(String date) {
		Pattern p = Pattern.compile("\\d{4}\\-\\d{2}\\-\\d{2}\\s\\d{2}\\:\\d{2}\\:\\d{2}\\.\\d{1}");
		Matcher m = p.matcher(date);
		return m.find();
	}

	public static boolean isYMDHMS(String date) {
		Pattern p = Pattern.compile("\\d{4}\\-\\d{2}\\-\\d{2}\\s\\d{2}\\:\\d{2}\\:\\d{2}");
		Matcher m = p.matcher(date);
		return m.find();
	}

	public static boolean isYMD(String date) {
		Pattern p = Pattern.compile("\\d{4}\\-\\d{2}\\-\\d{2}");
		Matcher m = p.matcher(date);
		return m.find();
	}

	/**
	 * "yyyy-MM-dd hh:mm:ss"형의 날짜를 반환한다.
	 * @return yyyy-MM-dd hh:mm:ss 형의 날짜
	 */
	public static String getThisDate() {
		return getThisDate("yyyy-MM-dd hh:mm:ss");
	}

	/**
	 * 인자로 들어오는 형식의 날짜를 반환한다.
	 * @param s 날짜 형식
	 * @return 인자로 들어오는 형식의 날짜
	 */
	public static String getThisDate(String s) {
		SimpleDateFormat simpledateformat = new SimpleDateFormat(s, Locale.US);
		return simpledateformat.format(new Date());
	}

	/**
	 * 날짜 데이터가 오늘보다 과거인지 미래인지 체크한다.
	 * @param date yyyy-MM-dd 형식의 문자열 날짜 데이터
	 * @return 과거면 false, 같거나 미래면 true
	 */
	public static boolean isAway(String s) {
		return isAway(s.substring(0, 4), s.substring(5, 7), s.substring(8));
	}

	/**
	 * 날짜 데이터가 오늘보다 과거인지 미래인지 체크한다.
	 * @param yyyy 년(年)
	 * @param mm 월(月)
	 * @param dd 일(日)
	 * @return 과거면 false, 같거나 미래면 true
	 */
	public static boolean isAway(String yyyy, String mm, String dd) {
		int y = parseInt(yyyy);
		int m = parseInt(mm);
		int d = parseInt(dd);
		return isAway(y, m, d);
	}

	/**
	 * 날짜 데이터가 오늘보다 과거인지 미래인지 체크한다.
	 * @param y 년(年)
	 * @param m 월(月)
	 * @param d 일(日)
	 * @return 과거면 false, 같거나 미래면 true
	 */
	public static boolean isAway(int y, int m, int d) {

		boolean dateChk = false;

		GregorianCalendar rightNow = new GregorianCalendar();
		GregorianCalendar fromDate = new GregorianCalendar(y, (m - 1), d);

		if(fromDate.before(rightNow)) {
			dateChk = false; // date가 rightNow보다 과거에있다.
		} else {
			dateChk = true; // date가 rightNow보다 미래에있다.
		}

		return dateChk;
	}

	// --------------------------------------------------------------------------
	// CRYPTO 관련 UTIL
	// --------------------------------------------------------------------------

	public static String getMD5String(String str) throws Exception {
		MessageDigest md = MessageDigest.getInstance("MD5");
		md.update(str.trim().getBytes());
		BASE64Encoder encoder = new BASE64Encoder();
		return encoder.encode(md.digest());
	}

	public static String getMD5HexString(String str) throws Exception {
		MessageDigest md = MessageDigest.getInstance("MD5");
		md.update(str.getBytes());
		byte[] byteArray = md.digest();
		String hexString = "";
		String eip;

		for (int i = 0; i < byteArray.length; i++) {
			eip = "" + Integer.toHexString(byteArray[i] & 0x000000ff);
			// System.out.println(i + " : " + eid);
			if(eip.length() < 2)
				eip = "0" + eip;
			hexString = hexString + eip;
		}

		return hexString;
	}

	public static byte[] decodeBASE64(String source) throws Exception {
		BASE64Decoder decoder = new BASE64Decoder();
		return decoder.decodeBuffer(source);
	}

	public static String encodeBASE64(byte[] b) {
		BASE64Encoder encoder = new BASE64Encoder();
		return encoder.encode(b);
	}

	public static String decodeURL(String source) throws Exception {
		return URLDecoder.decode(source, "UTF-8");
	}

	public static String encodeURL(String source) throws Exception {
		return URLEncoder.encode(source, "UTF-8");
	}

	public static String encodeURL(String source, String characterSet) throws Exception {
		return URLEncoder.encode(source, characterSet);
	}

	public static String decodeHTMLEscapeCharacter(String source) {

		final String ESC_LT = "&lt;";
		final String ESC_GT = "&gt;";
		final String ESC_AMP = "&amp;";

		source.replaceAll(ESC_LT, "<");
		source.replaceAll(ESC_GT, ">");
		source.replaceAll(ESC_AMP, "&");

		return source;
	}

	// --------------------------------------------------------------------------
	// FILE 관련 UTIL
	// --------------------------------------------------------------------------

	public static String readFile(String filePath) throws Exception {

		BufferedReader in = null;

		try {

			in = new BufferedReader(new FileReader(filePath));
			String str;
			StringBuffer source = new StringBuffer();
			while ((str = in.readLine()) != null) {
				source.append(str).append("\n");
			}

			return source.toString();

		} catch (FileNotFoundException e) {

			e.printStackTrace();
			throw new Exception("Reading File Not Found: " + e.getMessage());

		} catch (IOException e) {

			e.printStackTrace();
			throw new Exception("Readomg File Read Failed: " + e.getMessage());

		} finally {
			try {
				in.close();
			} catch (IOException ignored) {}
		}
	}

	public static void writeFile(String source, String filePath) throws Exception {
		writeFile(source, new File(filePath));
	}

	public static void writeFile(String source, File f) throws Exception {

		PrintWriter pw = null;
		try {

			pw = new PrintWriter(new FileWriter(f, false), true);
			pw.println(source);
			pw.flush();

		} catch (IOException e) {

			e.printStackTrace();
			throw new Exception("File Create Failed: " + e.getMessage());

		} finally {
			pw.close();
		}
	}

	public static ClassLoader getTCL() throws IllegalAccessException, InvocationTargetException {

		Method method = null;
		try {
			method = Thread.class.getMethod("getContextClassLoader");
		} catch (NoSuchMethodException e) {
			return null;
		}
		return (ClassLoader) method.invoke(Thread.currentThread());
	}

	public static File getFileInClassPath(String filename) {

		URL url = null;
		try {
			ClassLoader classLoader = CommonUtil.getTCL();
			url = classLoader.getResource(filename);
		} catch (Exception e) {
			url = ClassLoader.getSystemResource(filename);
		}

		return new File(url.getFile());
	}

	public static void copy(String source, String target) throws FileNotFoundException, IOException {
		copy(new File(source), new File(target));
	}

	public static void copy(File source, File target) throws FileNotFoundException, IOException {
		FileChannel srcChannel = new FileInputStream(source).getChannel();
		FileChannel dstChannel = new FileOutputStream(target).getChannel();
		dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
		srcChannel.close();
		dstChannel.close();
	}

	/**
	 * 파일의 확장자를 구합니다.
	 * @param fileName 파일 이름
	 * @return
	 */
	public static String getFileExtention(String fileName) {
		if(CommonUtil.isNullOrSpace(fileName)) {
			return "";
		} else {
			return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
		}
	}

	/**
	 * 파일만 삭제
	 * @param fileName 파일 이름
	 * @return
	 */
	public static void deleteFile(String filePath) {
		File file = new File(filePath);
		if(file.exists() && !file.isDirectory()) {
			file.delete();
		}
	}

	// --------------------------------------------------------------------------
	// Stream 관련 UTIL
	// --------------------------------------------------------------------------

	public static String getReader2String(Reader reader) throws IOException {

		int bytesRead = 0;
		char[] buffer = new char[1024];
		StringBuffer sb = new StringBuffer();
		while ((bytesRead = reader.read(buffer, 0, 1024)) != -1) {
			sb.append(buffer, 0, bytesRead);
		}
		reader.close();
		reader = null;
		return sb.toString();
	}

	public static final byte[] stream2bytes(InputStream is) {
		ByteArrayOutputStream out = null;
		try {
			out = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int size;
			while ((size = is.read(buffer)) > 0) {
				out.write(buffer, 0, size);
				out.flush();
			}
			return out.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(out != null)
				try {
					out.close();
					out = null;
				} catch (Exception ignored) {}
		}
		return null;
	}

	/*
	 * String url =
	 * "http://www.freeegg.com/contents/mov/i/e/_view.egg?contentsIdx=114579&chart=d&stDate=20080428&type=10"
	 * ; getStringBetween(url, 2, 3, "/"); -> www.freeegg.com
	 * getStringBetween(url, 3, 0, "/"); ->
	 * contents/mov/i/e/_view.egg?contentsIdx
	 * =114579&chart=d&stDate=20080428&type=10
	 */

	public static String getStringBetween(String str, int first, int end, String seperator) {
		int pos = 0;
		int loopCount = 0;
		int startPos = 0;
		int endPos = 0;
		while (true) {
			pos = str.indexOf(seperator, pos);
			loopCount++;
			if(loopCount == first) {
				if(first == 0) {
					startPos = 0;
					endPos = pos;
					break;
				} else if(end == 0) {
					startPos = pos;
					endPos = str.length();
					break;
				} else {
					startPos = pos;
				}
			} else if(loopCount == end)
				endPos = pos;
			if(pos < 0)
				break;
			else
				pos++;
		}

		return str.substring(startPos + 1, endPos);
	}

}
